-
Notifications
You must be signed in to change notification settings - Fork 5
feat: introduce new API connection management #1981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Caution Review failedThe pull request is closed. WalkthroughThis refactoring shifts API management from a synchronous setter-based pattern to an asynchronous, deduplicating connection manager. The endpointManager2 class is removed, APIContext is redesigned with async getApi, and EndpointManager is simplified to track endpoints per genesis hash instead of per address. ApiProvider now handles connection deduplication and auto-mode resolution internally. Changes
Sequence Diagram(s)sequenceDiagram
participant Consumer as Component/<br/>Hook
participant API as APIContext/<br/>getApi
participant Dedup as Dedup Logic
participant Endpoint as EndpointManager
participant Conn as Connection
Note over Consumer,Conn: New Async Flow
Consumer->>API: getApi(genesisHash, endpoints)
activate API
alt Pending Connection Exists
API->>Dedup: Check requestedQueue
Dedup-->>API: Return pending promise
else New Request
API->>Dedup: Add to requestedQueue
API->>Endpoint: Determine endpoint
activate Endpoint
Endpoint-->>API: endpoint
deactivate Endpoint
API->>Conn: Connect (WebSocket /<br/>LightClient)
activate Conn
Conn-->>API: ApiPromise
deactivate Conn
API->>Dedup: Resolve pending<br/>promises
API-->>Consumer: Promise<ApiPromise>
end
deactivate API
sequenceDiagram
participant Consumer as Component
participant OldAPI as Old<br/>useApi
participant OldProvider as Old<br/>APIProvider
participant NewAPI as New<br/>useApi
participant NewProvider as New<br/>ApiProvider
Note over Consumer,NewProvider: Old vs New Pattern
rect rgb(200, 220, 255)
Note over OldAPI,OldProvider: Old Synchronous Pattern
Consumer->>OldAPI: useApi(genesisHash)
OldAPI->>OldProvider: Read apis[genesisHash]
OldProvider-->>OldAPI: ApiPromise | undefined
OldAPI-->>Consumer: Immediate result
end
rect rgb(220, 240, 220)
Note over NewAPI,NewProvider: New Async Pattern
Consumer->>NewAPI: useApi(genesisHash)
NewAPI->>NewProvider: getApi(genesisHash, endpoints)
activate NewProvider
NewProvider->>NewProvider: Check dedup queue
NewProvider->>NewProvider: Connect to endpoint
NewProvider-->>NewAPI: Promise<ApiPromise>
deactivate NewProvider
NewAPI-->>Consumer: api (after await)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
🧹 Nitpick comments (3)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (3)
9-9: Import naming could be clearer after class rename.The import statement imports the class as
EndpointManagerfromendpointManager2.ts, but the class was renamed toEndpointManager2in that file. While this works, it might be clearer to import it with its actual name for consistency.Consider importing with the actual class name:
-import EndpointManager from '@polkadot/extension-polkagate/src/class/endpointManager2'; +import EndpointManager2 from '@polkadot/extension-polkagate/src/class/endpointManager2';And update line 16:
-const endpointManager = new EndpointManager(); +const endpointManager = new EndpointManager2();
180-207: Consider handling non-WSS endpoints.The function only handles auto-mode and WSS endpoints (lines 198-206). If an endpoint doesn't start with 'wss' and isn't auto-mode, it will be marked as requested but no connection will be initiated. This could leave pending connections unresolved.
Consider adding a fallback or error handling for unsupported endpoints:
if (endpoint.endpoint.startsWith('wss')) { connectToEndpoint(endpoint.endpoint).catch(console.error); + } else { + console.warn('Unsupported endpoint type:', endpoint.endpoint); + resolvePendingConnections(genesisHash, undefined); } }, [connectToEndpoint, handleAutoMode]);
64-269: Consider adding cleanup for disconnected APIs.The component maintains a map of APIs but doesn't clean up disconnected APIs or disconnect them on unmount. This could lead to memory leaks over time, especially if users frequently switch between chains.
Consider adding a cleanup effect:
useEffect(() => { return () => { // Cleanup on unmount Object.values(apis).forEach((apiList) => { apiList?.forEach(({ api }) => { if (api?.isConnected) { api.disconnect().catch(console.error); } }); }); }; }, []); // Empty deps - only run on unmountAdditionally, consider periodically removing disconnected APIs from the map to prevent unbounded growth.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
packages/extension-polkagate/src/class/endpointManager2.ts(1 hunks)packages/extension-polkagate/src/components/contexts.tsx(1 hunks)packages/extension-polkagate/src/hooks/useApi.ts(1 hunks)packages/extension-polkagate/src/hooks/useEndpoint.ts(2 hunks)packages/extension-polkagate/src/util/constants.ts(1 hunks)packages/extension-polkagate/src/util/types.ts(1 hunks)packages/extension-ui/src/Popup/contexts/ApiProvider.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-08-31T05:19:02.468Z
Learnt from: AMIRKHANEF
PR: PolkaGate/extension#1467
File: packages/extension-polkagate/src/util/types.ts:0-0
Timestamp: 2024-08-31T05:19:02.468Z
Learning: The `SavedEndpoint` interface in the `packages/extension-polkagate/src/util/types.ts` file has a property named `isOnManual`, which indicates whether the endpoint is in manual mode.
Applied to files:
packages/extension-polkagate/src/hooks/useEndpoint.tspackages/extension-polkagate/src/util/constants.ts
🧬 Code graph analysis (4)
packages/extension-polkagate/src/hooks/useEndpoint.ts (1)
packages/extension-polkagate/src/util/constants.ts (1)
AUTO_MODE_DEFAULT_ENDPOINT(267-272)
packages/extension-polkagate/src/components/contexts.tsx (1)
packages/extension-polkagate/src/util/types.ts (1)
APIsContext(813-816)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (5)
packages/extension-polkagate/src/util/constants.ts (2)
AUTO_MODE(262-265)AUTO_MODE_DEFAULT_ENDPOINT(267-272)packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(12-94)packages/extension-polkagate/src/util/types.ts (3)
APIs(811-811)DropdownOption(877-880)EndpointType(897-902)packages/extension-polkagate/src/util/misc.ts (1)
fastestConnection(167-193)packages/extension-polkagate/src/components/contexts.tsx (1)
APIContext(39-39)
packages/extension-polkagate/src/hooks/useApi.ts (2)
packages/extension-polkagate/src/components/contexts.tsx (1)
APIContext(39-39)packages/extension-polkagate/src/hooks/useEndpoints.ts (1)
useEndpoints(21-82)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: Analyze (javascript)
🔇 Additional comments (9)
packages/extension-polkagate/src/class/endpointManager2.ts (1)
14-14: Good refactor: Class name now follows naming conventions.The class name has been updated to PascalCase (
EndpointManager2), which aligns with JavaScript/TypeScript naming conventions for classes.packages/extension-polkagate/src/components/contexts.tsx (1)
16-16: LGTM: APIContext updated to new async pattern.The default value correctly reflects the new API surface, replacing the synchronous
setItwith an asynchronousgetApithat returns a resolved Promise. This aligns with the PR's shift to an asynchronous getter pattern.packages/extension-polkagate/src/util/types.ts (1)
813-816: Breaking change: APIsContext interface updated to async pattern.The interface now exposes
getApi(async) instead ofsetIt(sync), which is a breaking change to the public API surface. This is aligned with the PR objectives and enables better connection management with deduplication and async resolution.packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (5)
70-84: Good use of refs for concurrent request management.The implementation correctly uses refs (
requestedQueue,pendingConnections,apisRef) to manage mutable state in async callbacks without causing excessive re-renders. The sync effect (lines 82-84) ensuresapisRefstays current with the state.
102-110: LGTM: Clean resolution of pending connections.The function correctly resolves all waiting promises for a genesis hash and cleans up the entry afterward.
112-138: LGTM: Proper API deduplication and normalization.The function correctly handles deduplication by removing existing APIs with the same endpoint and clears other auto-mode APIs when appropriate. The pending connection resolution ensures all waiters are notified.
140-159: LGTM: Auto-mode endpoint selection logic.The auto-mode handling correctly filters WSS endpoints, uses
fastestConnectionto select the best endpoint, and handles errors gracefully by resolving pending connections withundefined.
264-269: LGTM: Clean provider implementation.The provider correctly exposes the
apisstate andgetApifunction through the context.packages/extension-polkagate/src/hooks/useApi.ts (1)
11-26: LGTM: Clean simplification of useApi hook.The hook has been simplified to rely on the new
getApicontext method. The implementation correctly:
- Uses
getApifrom context (which is memoized in ApiProvider)- Fetches endpoints for the genesis hash
- Manages local API state via
useState- Updates when dependencies change
The new signature
useApi(genesisHash)is cleaner than the previous version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/extension/package.json (1)
8-9: Update homepage URL to point to main branch.The repository's default branch is
main, but the homepage URL in package.json still references/tree/master. Update line 8:"homepage": "https://github.com/polkagate/extension/tree/main/packages/extension#readme",
♻️ Duplicate comments (4)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
86-93: Don’t hardcode isAuto; persist manual vs auto correctly.updateEndpoint always sets isAuto: true, which is wrong for manual selections and prevents accurate mode tracking.
-const updateEndpoint = useCallback((chainKey: string, selectedEndpoint: string, cbFunction?: () => void) => { +const updateEndpoint = useCallback(( + genesisHash: string, + selectedEndpoint: string, + isAuto: boolean, + cbFunction?: () => void +) => { try { const newEndpoint = { checkForNewOne: false, endpoint: selectedEndpoint, - isAuto: true, + isAuto, timestamp: Date.now() }; - - endpointManager.set(chainKey, newEndpoint); + endpointManager.set(genesisHash, newEndpoint); cbFunction?.(); } catch (error) { console.error(error); } }, []);And pass isAuto=true only from auto flow (see Line 158), false from manual flow.
161-178: On error, resolve the correct pending and free resources.connectToEndpoint guesses a genesisHash and may resolve the wrong pending; also leaks wsProvider on failure.
-const connectToEndpoint = useCallback(async (endpointToConnect: string) => { +const connectToEndpoint = useCallback(async (genesisHash: string, endpointToConnect: string) => { try { const wsProvider = new WsProvider(endpointToConnect); const newApi = await ApiPromise.create({ provider: wsProvider }); handleNewApi(newApi, endpointToConnect); } catch (error) { console.error('Connection error:', error); - // Resolve pending with undefined on error - const genesisHash = Object.keys(pendingConnections.current).find((key) => - pendingConnections.current[key].length > 0 - ); - - if (genesisHash) { - resolvePendingConnections(genesisHash, undefined); - } + // Resolve only the intended pending + resolvePendingConnections(genesisHash, undefined); } }, [handleNewApi, resolvePendingConnections]);Also update the call site (Line 205) to pass genesisHash and disconnect the provider on failure if instantiated before create().
- connectToEndpoint(endpoint.endpoint).catch(console.error); + connectToEndpoint(genesisHash, endpoint.endpoint).catch(console.error);
236-241: Pending keyed only by genesis can resolve a different endpoint.Known issue (TODO). Track pending by both genesis and endpoint (or store endpoint on each entry and validate), so callers don’t receive an API for a different endpoint.
-const pendingConnections = useRef< - Record<string, { - promise: Promise<ApiPromise | undefined>; - resolve(api: ApiPromise | undefined): void; - }[]> ->({}); +type PendingEntry = { promise: Promise<ApiPromise | undefined>; resolve(api: ApiPromise | undefined): void }; +const pendingConnections = useRef<Record<string, Record<string, PendingEntry>>>({});Adjust creation/lookup:
-if (pendingConnections.current[genesisHash]?.length > 0) { - return pendingConnections.current[genesisHash][0].promise; -} +const pendByEp = pendingConnections.current[genesisHash]; +if (pendByEp?.[endpoint.endpoint!]) { + return pendByEp[endpoint.endpoint!].promise; +} @@ -if (!pendingConnections.current[genesisHash]) { - pendingConnections.current[genesisHash] = []; -} - -pendingConnections.current[genesisHash].push({ - promise, - resolve: resolvePromise -}); +pendingConnections.current[genesisHash] ||= {}; +pendingConnections.current[genesisHash][endpoint.endpoint!] = { promise, resolve: resolvePromise };And in resolvePendingConnections, remove by endpoint:
-const resolvePendingConnections = useCallback((genesisHash: string, api: ApiPromise | undefined) => { - const pending = pendingConnections.current[genesisHash]; - if (pending) { - pending.forEach(({ resolve }) => resolve(api)); - delete pendingConnections.current[genesisHash]; - } -}, []); +const resolvePendingConnections = useCallback((genesisHash: string, api: ApiPromise | undefined, endpoint?: string) => { + const pendByEp = pendingConnections.current[genesisHash]; + if (!pendByEp) return; + if (endpoint && pendByEp[endpoint]) { + pendByEp[endpoint].resolve(api); + delete pendByEp[endpoint]; + } else { + // fallback: resolve all + Object.values(pendByEp).forEach(({ resolve }) => resolve(api)); + delete pendingConnections.current[genesisHash]; + } +}, []);Call with endpoint where available (e.g., in handleNewApi and error paths).
140-160: updateEndpoint call should pass isAuto=true explicitly.After changing updateEndpoint’s signature, update this call.
- updateEndpoint(genesisHash, selectedEndpoint, () => handleNewApi(api, selectedEndpoint, true)); + updateEndpoint(genesisHash, selectedEndpoint, true, () => handleNewApi(api, selectedEndpoint, true));
🧹 Nitpick comments (2)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (2)
102-110: resolvePendingConnections should accept endpoint to avoid cross-resolution.Update callers to pass the endpoint so only that pending resolves.
-const resolvePendingConnections = useCallback((genesisHash: string, api: ApiPromise | undefined) => { +const resolvePendingConnections = useCallback((genesisHash: string, api: ApiPromise | undefined, endpoint?: string) => { const pending = pendingConnections.current[genesisHash]; - if (pending) { - pending.forEach(({ resolve }) => resolve(api)); - delete pendingConnections.current[genesisHash]; - } + // ...see diff in the main pending refactor comment... }, []);Update calls at Lines 137, 153, 175 accordingly.
70-77: Prefer Set for requestedQueue.Use Set per genesis for O(1) checks and easy deletion.
-const requestedQueue = useRef<Record<string, string[]>>({}); +const requestedQueue = useRef<Record<string, Set<string>>>({});Then adapt includes/push/filter usages accordingly.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx(1 hunks)packages/extension/package.json(1 hunks)packages/extension/src/packageInfo.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
packages/extension/src/packageInfo.ts (5)
packages/extension-base/src/packageInfo.ts (1)
packageInfo(6-6)packages/extension-chains/src/packageInfo.ts (1)
packageInfo(6-6)packages/extension-dapp/src/packageInfo.ts (1)
packageInfo(6-6)packages/extension-inject/src/packageInfo.ts (1)
packageInfo(6-6)packages/extension-compat-metamask/src/packageInfo.ts (1)
packageInfo(6-6)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
packages/extension-polkagate/src/util/constants.ts (2)
AUTO_MODE(262-265)AUTO_MODE_DEFAULT_ENDPOINT(267-272)packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(12-94)packages/extension-polkagate/src/util/types.ts (3)
APIs(811-811)DropdownOption(877-880)EndpointType(897-902)packages/extension-polkagate/src/util/misc.ts (1)
fastestConnection(167-193)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
packages/extension/src/packageInfo.ts (1)
6-6: Version bump looks good.Matches the package.json update. Ensure this file was generated via @polkadot/dev, not hand-edited.
packages/extension/package.json (1)
18-18: Version bump acknowledged.No other dependency/config changes. LGTM.
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (1)
228-235: Return only an API that matches the selected mode/endpoint.Currently any connected API for the genesis is returned, ignoring the chosen endpoint. Respect auto vs manual and endpoint equality.
-const availableApi = apiList?.find(({ api }) => api?.isConnected); +const availableApi = apiList?.find(({ api, endpoint: ep }) => + api?.isConnected && + (isAutoMode(endpoint.endpoint) + ? isAutoMode(ep) + : ep === endpoint.endpoint) +);Please verify ApiPromise exposes isConnected in your @polkadot/api version; otherwise check provider.isConnected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (6)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (6)
8-16: Import alias mismatches exported class name.The file
endpointManager2.tsexportsEndpointManager2as its default export, but line 9 aliases it asEndpointManager. This creates confusion about which version is in use and masks the actual class name.Align the import with the exported name:
-import EndpointManager from '@polkadot/extension-polkagate/src/class/endpointManager2'; +import EndpointManager2 from '@polkadot/extension-polkagate/src/class/endpointManager2';Then update line 16 and any other usages:
-const endpointManager = new EndpointManager(); +const endpointManager = new EndpointManager2();
86-100: HardcodedisAuto: trueincorrect for manual endpoint selections.The
updateEndpointfunction always setsisAuto: true(line 91), even when called for manually-selected endpoints. This misrepresents the selection mode and could cause incorrect behavior in endpoint management.Add an
isAutoparameter:-const updateEndpoint = useCallback((chainKey: string, selectedEndpoint: string, cbFunction?: () => void) => { +const updateEndpoint = useCallback((chainKey: string, selectedEndpoint: string, isAuto: boolean, cbFunction?: () => void) => { try { const newEndpoint = { checkForNewOne: false, endpoint: selectedEndpoint, - isAuto: true, + isAuto, timestamp: Date.now() };Then update the call site at line 158:
-updateEndpoint(genesisHash, selectedEndpoint, () => handleNewApi(api, selectedEndpoint, true)); +updateEndpoint(genesisHash, selectedEndpoint, true, () => handleNewApi(api, selectedEndpoint, true));
112-138: Clear requestedQueue flags after successful connection.The
requestedQueuemarks endpoints as requested but entries are never removed after a successful connection. This prevents future retry attempts for the same endpoint and causes a resource leak.Clear the request flag after handling:
const handleNewApi = useCallback((api: ApiPromise, endpoint: string, onAutoMode?: boolean) => { const genesisHash = String(api.genesisHash.toHex()); setApis((prevApis) => { // ... existing logic ... }); + // Clear request mark for this endpoint + const rq = requestedQueue.current[genesisHash]; + if (rq) { + requestedQueue.current[genesisHash] = rq.filter((e) => e !== endpoint); + } + // Resolve all waiting promises resolvePendingConnections(genesisHash, api); }, [resolvePendingConnections]);
174-201: Manual endpoint selections are not persisted.The manual selection path (line 199) calls
connectToEndpointbut never persists the chosen endpoint viaupdateEndpoint, unlike the auto-mode path. This means manual selections won't be remembered across sessions or component remounts.Persist manual selections and handle failures:
if (endpoint.endpoint.startsWith('wss')) { - connectToEndpoint(genesisHash, endpoint.endpoint).catch(console.error); + connectToEndpoint(genesisHash, endpoint.endpoint) + .then(() => { + // Persist manual selection (isAuto: false) + // Note: updateEndpoint signature needs the isAuto parameter added first + }) + .catch((e) => { + console.error(e); + // Clear request mark on failure + const rq = requestedQueue.current[genesisHash]; + if (rq) { + requestedQueue.current[genesisHash] = rq.filter((v) => v !== endpoint.endpoint); + } + }); }Note: This requires fixing the
updateEndpointsignature first to accept anisAutoparameter.
208-214: Avoid writing default AUTO_MODE to storage inside getApi.Writing the default
AUTO_MODE_DEFAULT_ENDPOINTto storage immediately (lines 211-213) when no endpoint is found can overwrite user settings during startup before storage load completes in EndpointManager2. This mutation should not happen in a getter function.Treat the default as ephemeral:
let endpoint = endpointManager.get(genesisHash); if (!endpoint) { - endpoint = { ...AUTO_MODE_DEFAULT_ENDPOINT, timestamp: Date.now() }; - - endpointManager.set(genesisHash, endpoint); + // Use as in-memory default; do not persist yet + endpoint = { ...AUTO_MODE_DEFAULT_ENDPOINT, timestamp: Date.now() }; }Let the endpoint be persisted later by
handleAutoModeviaupdateEndpointwhen auto-mode actually selects an endpoint, or by explicit user action for manual selections.
230-235: Pending connection may resolve to wrong endpoint.The
pendingConnectionsstructure is keyed only bygenesisHash, not endpoint. IfgetApiis called multiple times with different endpoints for the same genesis hash while a connection is pending, all callers share the first pending promise, which may resolve to an API for a different endpoint than requested.The TODO comment at line 230 correctly identifies this issue. Consider one of these solutions:
Solution 1: Track endpoint in pending connections:
const pendingConnections = useRef< - Record<string, { + Record<string, Record<string, { promise: Promise<ApiPromise | undefined>; resolve(api: ApiPromise | undefined): void; - }[]> + }>> >({});Then key by both
genesisHashandendpoint, checking that the pending endpoint matches before returning the promise.Solution 2: Include endpoint validation in the existing array structure by storing endpoint alongside each pending entry and filtering for matching endpoint before returning the promise.
🧹 Nitpick comments (1)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (1)
237-252: Consider standard Promise pattern for clarity.The pattern of initializing
resolvePromisewith a no-op (line 240) then overwriting it in the executor is safe but unconventional. A more standard pattern would eliminate the intermediate assignment.- let resolvePromise: (api: ApiPromise | undefined) => void = () => undefined; - const promise = new Promise<ApiPromise | undefined>((resolve) => { - resolvePromise = resolve; - }); + const entry = (() => { + let resolvePromise: (api: ApiPromise | undefined) => void; + const promise = new Promise<ApiPromise | undefined>((resolve) => { + resolvePromise = resolve; + }); + return { promise, resolve: resolvePromise! }; + })(); if (!pendingConnections.current[genesisHash]) { pendingConnections.current[genesisHash] = []; } - pendingConnections.current[genesisHash].push({ - promise, - resolve: resolvePromise - }); + pendingConnections.current[genesisHash].push(entry);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
packages/extension-polkagate/src/hooks/useEndpoint.ts(2 hunks)packages/extension-ui/src/Popup/contexts/ApiProvider.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/extension-polkagate/src/hooks/useEndpoint.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
packages/extension-polkagate/src/util/constants.ts (2)
AUTO_MODE(262-265)AUTO_MODE_DEFAULT_ENDPOINT(267-272)packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(12-94)packages/extension-polkagate/src/util/types.ts (3)
APIs(811-811)DropdownOption(877-880)EndpointType(897-902)packages/extension-polkagate/src/util/misc.ts (1)
fastestConnection(167-193)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Analyze (javascript)
- GitHub Check: build
🔇 Additional comments (4)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
18-84: Excellent documentation and state management structure.The comprehensive JSDoc clearly explains the complex connection lifecycle, deduplication strategy, and caveats. The use of refs (
apisRef,requestedQueue,pendingConnections) alongside state is appropriate for managing async operations without stale closures.
102-110: LGTM: Clean promise resolution and cleanup.The function correctly resolves all pending promises for a genesis hash and removes the entry to prevent memory leaks.
140-159: Auto-mode logic is sound.The function appropriately checks for existing auto-mode APIs, filters WSS endpoints, and uses
fastestConnectionto select the best endpoint. The call toupdateEndpointat line 158 should passisAuto: trueas a parameter once the hardcoded value is fixed (addressed in separate comment).
260-264: Provider correctly exposes new async API.The context now exposes
{ apis, getApi }instead of the previous{ apis, setIt }, completing the transition from synchronous setter to asynchronous getter pattern as described in the PR objectives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/extension-polkagate/src/fullscreen/settings/partials/useEndpointsSetting.ts (1)
112-124: Auto-apply runs on any state change (including delays) → unintended writes/close.Comparing whole state by reference triggers onApply for unrelated updates (e.g., endpointsDelay). Limit to relevant fields or track a dirty flag.
- if (prevStateRef.current && prevStateRef.current !== state) { - onApply(); - } + if (prevStateRef.current) { + const prev = prevStateRef.current; + const changed = + prev.isOnAuto !== state.isOnAuto || + prev.mayBeEnabled !== state.mayBeEnabled || + prev.maybeNewEndpoint !== state.maybeNewEndpoint; + if (changed) onApply(); + }
♻️ Duplicate comments (6)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (6)
109-135: Clear requestedQueue on success to allow future retries.setApis((prevApis) => { @@ }); + // unmark request for this endpoint + const rq = requestedQueue.current[genesisHash]; + if (rq) { + requestedQueue.current[genesisHash] = rq.filter((e) => e !== endpoint); + } resolvePendingConnections(genesisHash, api);
158-169: On connection error, clear requestedQueue for this endpoint.} catch (error) { console.error('Connection error:', error); - // Resolve pending with undefined on error + // Clear request mark on failure + const rq = requestedQueue.current[genesisHash]; + if (rq) { + requestedQueue.current[genesisHash] = rq.filter((e) => e !== endpointToConnect); + } + // Resolve pending with undefined on error resolvePendingConnections(genesisHash, undefined); }
171-198: Persist manual selections and cleanup request flags.- if (isAutoMode(endpoint.endpoint)) { + if (isAutoMode(endpoint.endpoint)) { handleAutoMode(genesisHash, endpoints).catch(console.error); return; } if (endpoint.endpoint.startsWith('wss')) { - connectToEndpoint(genesisHash, endpoint.endpoint).catch(console.error); + // Persist manual selection immediately + updateEndpoint(genesisHash, endpoint.endpoint, false); + connectToEndpoint(genesisHash, endpoint.endpoint) + .catch((e) => { + console.error(e); + // clear request mark on failure + const rq = requestedQueue.current[genesisHash]; + if (rq) { + requestedQueue.current[genesisHash] = rq.filter((v) => v !== endpoint.endpoint); + } + }); } -}, [connectToEndpoint, handleAutoMode]); +}, [connectToEndpoint, handleAutoMode, updateEndpoint]);
83-91: Don’t hardcode isAuto: true in updateEndpoint.-const updateEndpoint = useCallback((chainKey: string, selectedEndpoint: string, cbFunction?: () => void) => { +const updateEndpoint = useCallback((chainKey: string, selectedEndpoint: string, isAuto: boolean, cbFunction?: () => void) => { try { const newEndpoint = { checkForNewOne: false, endpoint: selectedEndpoint, - isAuto: true, + isAuto, timestamp: Date.now() }; endpointManager.set(chainKey, newEndpoint); cbFunction?.(); } catch (error) { console.error(error); } -}, []); +}, []);Update callers accordingly (auto: true; manual: false).
200-211: Avoid writing default AUTO endpoint inside getApi; can clobber storage during init.- if (!endpoint) { - endpoint = { ...AUTO_MODE_DEFAULT_ENDPOINT, timestamp: Date.now() }; - endpointManager.set(genesisHash, endpoint); - } + if (!endpoint) { + // Use ephemeral default; do not persist here + endpoint = { ...AUTO_MODE_DEFAULT_ENDPOINT, timestamp: Date.now() }; + }Persist later via updateEndpoint after a successful auto/manual selection.
227-233: Pending promise keyed only by genesisHash can resolve wrong endpoint.Track endpoint in pending entries and only reuse/resolve matching ones.
- if (pendingConnections.current[genesisHash]?.length > 0) { - return pendingConnections.current[genesisHash][0].promise; - } + const pendings = pendingConnections.current[genesisHash] || []; + const epKey = endpoint.endpoint; // 'AutoMode' or concrete wss + const existing = pendings.find((p) => p.endpoint === epKey); + if (existing) return existing.promise;And update the type and push site:
-const pendingConnections = useRef< - Record<string, { +const pendingConnections = useRef< + Record<string, { promise: Promise<ApiPromise | undefined>; resolve(api: ApiPromise | undefined): void; - }[]> + endpoint: string; // key (AutoMode or wss) + }[]> >({});At push:
- pendingConnections.current[genesisHash].push({ promise, resolve: resolvePromise }); + pendingConnections.current[genesisHash].push({ promise, resolve: resolvePromise, endpoint: epKey });Update resolve to accept optional endpoint to target specific waiters.
🧹 Nitpick comments (4)
packages/extension-polkagate/src/util/workers/getValidatorsInfo.js (2)
35-39: Avoid JSON deep-clone before postMessage; structured clone is automatic.postMessage already structured‑clones. JSON clone is slower and can lose types.
- return JSON.parse(JSON.stringify({ - current: nextElectedInfo, - eraIndex: Number(currentEra), - waiting: waiting.info - })); + return { + current: nextElectedInfo, + eraIndex: Number(currentEra?.toString?.() ?? currentEra), + waiting: waiting.info + };
40-44: Use console.error for failures.Use error log level to aid monitoring.
- } catch (error) { - console.log('something went wrong while getting validators info, err:', error); + } catch (error) { + console.error('something went wrong while getting validators info, err:', error);packages/extension-polkagate/src/fullscreen/settings/partials/useEndpointsSetting.ts (1)
144-157: Reset isFetching on failure; use finally.Currently, failures keep the endpoint marked “fetching” forever; no retries.
- endpoint && CalculateNodeDelay(endpoint) - .then((response) => { + endpoint && CalculateNodeDelay(endpoint) + .then((response) => { if (!response) { return; } - isFetching.current[endpoint] = false; dispatch({ payload: { delay: response.delay, endpoint: response.endpoint }, type: 'UPDATE_DELAY' }); - response.api?.disconnect().catch(console.error); - }) - .catch(console.error); + }) + .catch(console.error) + .finally(() => { isFetching.current[endpoint] = false; });packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (1)
219-225: Prefer returning api matching the saved endpoint (or auto) instead of any connected.Today it returns the first connected api, which may not match the current selection.
-const availableApi = apiList?.find(({ api }) => api?.isConnected); +const saved = endpointManager.get(genesisHash); +const availableApi = saved?.endpoint && !isAutoMode(saved.endpoint) + ? apiList?.find(({ endpoint, api }) => endpoint === saved.endpoint && api?.isConnected) + : apiList?.find(({ api }) => api?.isConnected);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
packages/extension-polkagate/src/class/endpointManager.ts(1 hunks)packages/extension-polkagate/src/class/endpointManager2.ts(0 hunks)packages/extension-polkagate/src/fullscreen/settings/partials/useEndpointsSetting.ts(2 hunks)packages/extension-polkagate/src/hooks/useEndpoint.ts(2 hunks)packages/extension-polkagate/src/util/getApi.ts(1 hunks)packages/extension-polkagate/src/util/workers/getValidatorsInfo.js(2 hunks)packages/extension-ui/src/Popup/contexts/ApiProvider.tsx(1 hunks)
💤 Files with no reviewable changes (1)
- packages/extension-polkagate/src/class/endpointManager2.ts
✅ Files skipped from review due to trivial changes (1)
- packages/extension-polkagate/src/util/getApi.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-08-31T05:19:02.468Z
Learnt from: AMIRKHANEF
PR: PolkaGate/extension#1467
File: packages/extension-polkagate/src/util/types.ts:0-0
Timestamp: 2024-08-31T05:19:02.468Z
Learning: The `SavedEndpoint` interface in the `packages/extension-polkagate/src/util/types.ts` file has a property named `isOnManual`, which indicates whether the endpoint is in manual mode.
Applied to files:
packages/extension-polkagate/src/hooks/useEndpoint.tspackages/extension-polkagate/src/class/endpointManager.ts
🧬 Code graph analysis (4)
packages/extension-polkagate/src/hooks/useEndpoint.ts (2)
packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(14-92)packages/extension-polkagate/src/util/constants.ts (1)
AUTO_MODE_DEFAULT_ENDPOINT(267-272)
packages/extension-polkagate/src/fullscreen/settings/partials/useEndpointsSetting.ts (1)
packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(14-92)
packages/extension-polkagate/src/class/endpointManager.ts (1)
packages/extension-polkagate/src/util/types.ts (1)
EndpointType(897-902)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
packages/extension-polkagate/src/util/constants.ts (2)
AUTO_MODE(262-265)AUTO_MODE_DEFAULT_ENDPOINT(267-272)packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(14-92)packages/extension-polkagate/src/util/types.ts (3)
APIs(811-811)DropdownOption(877-880)EndpointType(897-902)packages/extension-polkagate/src/util/misc.ts (1)
fastestConnection(167-193)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
packages/extension-polkagate/src/hooks/useEndpoint.ts (1)
42-45: Add initialization gate to EndpointManager before persisting defaults; the suggested refactor references non-existent components.The race condition is real:
loadFromStorage()is not awaited (fire-and-forget), soset()calls during initialization can race with pending storage loads. However, the suggested fix referencing "ApiProvider/Settings persist after success" doesn't exist in the codebase, and using onlysetEndpoint()(local state) breaks the subscription pattern—other code sites likeuseEndpointsSetting.tscallendpointManager.set()directly.The proper fix follows the pattern already established in
nftManager.ts: add anisInitializedflag andwaitForInitialization()method, then defer writes until after initial load completes. Alternatively, guard against writing defaults during the initialization window by checking a ready state before callingset().packages/extension-polkagate/src/util/workers/getValidatorsInfo.js (1)
23-29: Use consistent query scope: either all live or all historic queries.The code mixes live derive queries (
api.derive.staking.electedInfoandapi.derive.staking.waitingInfo) with a historic query (apiAt.query.staking.currentEra()at finalized head), which risks returning data from different states. Choose one approach:
- All live (simplest): Remove
apiAtand useapi.query.staking.currentEra()directly.- All historic: Use
apiAt.derive.staking.electedInfo()andapiAt.derive.staking.waitingInfo()if supported in your polkadot.js version.Suggested fix:
- const at = await api.rpc.chain.getFinalizedHead(); - const apiAt = await api.at(at); const [elected, waiting, currentEra] = await Promise.all([ api.derive.staking.electedInfo(infoProps), api.derive.staking.waitingInfo(infoProps), - apiAt.query['staking']['currentEra']() + api.query.staking.currentEra() ]);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (8)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (8)
83-97: HardcodedisAuto: truecauses incorrect mode tracking.As previously noted,
updateEndpointalways setsisAuto: trueregardless of whether the selection was automatic or manual. This breaks endpoint mode tracking.
99-107: Resolves all pending connections regardless of endpoint.As previously noted, this function resolves all pending promises for a genesisHash without checking if they're waiting for the same endpoint. This causes the issue mentioned in the TODO at line 227.
109-135: Missing cleanup ofrequestedQueueon successful connection.As previously noted, successful connections don't clear the
requestedQueueflag, preventing future reconnection attempts if the connection drops.
137-156: Short-circuit check never matches because saved endpoints are wss URLs.As previously noted, line 139 checks if any saved
endpointstring equalsAUTO_MODE.value, but saved APIs store wss URLs (line 155), not the "AutoMode" string. The check should queryendpointManager.get(genesisHash)?.isAutoor store anisAutoflag on each saved API entry.
158-169: Error handler doesn't clearrequestedQueueflag.As previously noted, when connection fails, the
requestedQueueflag remains set, blocking future retry attempts for this endpoint.
171-198: Manual endpoint selection not persisted; no error cleanup.As previously noted, the manual selection path (lines 195-197) doesn't persist the endpoint choice and doesn't clear
requestedQueueflags on connection failure.
207-211: Avoid persisting AUTO_MODE default insidegetApi.As previously noted, writing the default endpoint to storage during
getApican overwrite user settings during startup. Treat AUTO_MODE_DEFAULT_ENDPOINT as an ephemeral default and persist only after successful auto-selection or explicit user action.
227-232: Pending connections not keyed by endpoint.As previously noted and acknowledged by the TODO comment, pending promises are keyed only by
genesisHash. If multiple callers request different endpoints for the same genesis, they all receive the same promise, which may resolve to an API for the wrong endpoint.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
packages/extension-polkagate/src/util/constants.ts (2)
AUTO_MODE(262-265)AUTO_MODE_DEFAULT_ENDPOINT(267-272)packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(14-92)packages/extension-polkagate/src/util/types.ts (3)
APIs(811-811)DropdownOption(877-880)EndpointType(897-902)packages/extension-polkagate/src/util/misc.ts (1)
fastestConnection(167-193)
🔇 Additional comments (4)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
18-63: LGTM: Comprehensive documentation.The documentation clearly explains the connection management architecture, responsibilities, and caveats.
64-81: LGTM: State and ref initialization.The ref synchronization pattern correctly maintains current values for async callbacks.
257-262: LGTM: Clean provider implementation.The context correctly exposes the new async
getApiAPI surface.
8-10: ****The review comment incorrectly flags the import path. The code imports from
@polkadot/extension-polkagate/src/class/endpointManager, which is correct—there is noendpointManager2file. The actual fileendpointManager.tsexportsexport default class EndpointManageras expected. The import is valid.Likely an incorrect or invalid review comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (7)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (7)
83-97: Still hardcoded:isAuto: trueprevents correct tracking of manual selections.The function always sets
isAuto: trueon line 88, which was flagged in a previous review. Although the current call site (line 163) is in an auto-mode context, the function signature suggests general-purpose use. If future code callsupdateEndpointfor a manual selection, the mode will be incorrectly recorded.Apply the previously suggested fix:
-const updateEndpoint = useCallback((genesisHash: string, selectedEndpoint: string, cbFunction?: () => void) => { +const updateEndpoint = useCallback((genesisHash: string, selectedEndpoint: string, isAuto: boolean, cbFunction?: () => void) => { try { const newEndpoint = { checkForNewOne: false, endpoint: selectedEndpoint, - isAuto: true, + isAuto, timestamp: Date.now() };Update the call site at line 163:
-updateEndpoint(genesisHash, selectedEndpoint, () => handleNewApi(api, selectedEndpoint, true)); +updateEndpoint(genesisHash, selectedEndpoint, true, () => handleNewApi(api, selectedEndpoint, true));
99-116: Resolves all pending promises regardless of endpoint.Line 104 resolves every pending promise for the
genesisHash, even though they may be waiting for different endpoints. If caller A awaitsgetApi(genesis, endpointX)and caller B awaitsgetApi(genesis, endpointY)concurrently, resolving one will incorrectly resolve both with the same API instance.The request-flag clearing (lines 108-115) is correct, but the promise resolution needs similar selectivity. Consider tracking the endpoint in
pendingConnectionsand resolving only matching entries, as suggested in the previous review.
145-164:autoModeExistscheck never matches; duplicate auto-mode connections possible.Line 147 checks
isAutoMode(endpoint)on stored API entries, buthandleNewApisaves APIs with their actual wss endpoint string (line 130-133), not the"AutoMode"literal. This check always returns false, so repeatedhandleAutoModecalls for the same genesis will spawn redundant connections.As previously suggested, check the
EndpointManagerstate or store anisAutoflag alongside each saved API entry:- const autoModeExists = apisForGenesis?.some(({ endpoint }) => isAutoMode(endpoint)); + const storedEndpoint = endpointManager.get(genesisHash); + const autoModeExists = storedEndpoint?.isAuto === true && + (apisForGenesis?.some(({ api }) => api?.isConnected) ?? false);
201-203: Manual endpoint selections are not persisted.When a user manually selects a wss endpoint (lines 201-203), the connection is initiated but the selection is never saved via
updateEndpoint. On reload or future calls, the user's choice will be lost. This was flagged in a previous review and remains unaddressed.Persist the manual selection:
if (endpointValue.startsWith('wss')) { - connectToEndpoint(genesisHash, endpointValue).catch(console.error); + updateEndpoint(genesisHash, endpointValue, false); + connectToEndpoint(genesisHash, endpointValue).catch(console.error); }Add
updateEndpointto the dependency array:-}, [connectToEndpoint, handleAutoMode]); +}, [connectToEndpoint, handleAutoMode, updateEndpoint]);Note: This also requires fixing the
updateEndpointsignature to accept theisAutoparameter as noted earlier.
211-217: Still writing default endpoint to storage; marked as addressed but persists.Lines 213-216 write
AUTO_MODE_DEFAULT_ENDPOINTto storage when no endpoint is found. A previous review comment (marked as addressed) flagged this mutation, noting it can overwrite user settings during startup before storage loads. The issue remains in the current code.Treat the default as ephemeral:
let endpoint = endpointManager.get(genesisHash); if (!endpoint) { - endpoint = { ...AUTO_MODE_DEFAULT_ENDPOINT, timestamp: Date.now() }; - - endpointManager.set(genesisHash, endpoint); + endpoint = AUTO_MODE_DEFAULT_ENDPOINT; // use in-memory; do not persist yet }Persistence will happen later in
handleAutoModeviaupdateEndpoint(..., true)or on explicit user action.
219-223: Dead code:endpointis always defined after line 213.Lines 213-216 ensure
endpointis set toAUTO_MODE_DEFAULT_ENDPOINTwhen undefined. The subsequent check on line 219 is unreachable.Remove the dead code:
- if (!endpoint) { - console.warn('No endpoint found for', genesisHash); - - return Promise.resolve(undefined); - } -
233-238: Pending connection may resolve to API for a different endpoint.The TODO comment on line 233 correctly identifies the issue:
pendingConnectionsis keyed only bygenesisHash, so concurrent calls with different endpoints for the same genesis will share the same pending promise, potentially returning an API connected to the wrong endpoint.This issue was flagged in a previous review. Consider tracking the endpoint within
pendingConnections:const pendingConnections = useRef< - Record<string, { + Record<string, Record<string, { promise: Promise<ApiPromise | undefined>; resolve(api: ApiPromise | undefined): void; - }[]> + }>> >({});Then key by both
genesisHashandendpoint, or validate that the pending entry's endpoint matches the requested one before returning its promise.
🧹 Nitpick comments (2)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (2)
182-182: Redundant check:endpointManageris always defined.
endpointManageris initialized as a module-level constant (line 16), so the check!endpointManageron line 182 is always false. Remove this condition:- if (!endpointValue || !endpointManager) { + if (!endpointValue) { return; }
243-246: UnnecessaryresolvePromiseinitialization pattern.The comment on line 242 explains initializing
resolvePromisewith a noop "so it's always defined," but this is unnecessary. The Promise executor (line 244) runs synchronously, soresolvePromiseis assigned on line 245 before the variable is used on line 254.Simplify:
- // initialize resolvePromise with a noop so it's always defined for the push below, - // then overwrite it synchronously inside the Promise executor. - let resolvePromise: (api: ApiPromise | undefined) => void = () => undefined; + let resolvePromise: (api: ApiPromise | undefined) => void; const promise = new Promise<ApiPromise | undefined>((resolve) => { resolvePromise = resolve; });TypeScript's definite assignment analysis understands the Promise executor runs immediately.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (5)
packages/extension-polkagate/src/util/workers/getValidatorsInfo.js (2)
e(48-48)api(13-13)packages/extension-polkagate/src/util/constants.ts (2)
AUTO_MODE(262-265)AUTO_MODE_DEFAULT_ENDPOINT(267-272)packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(14-92)packages/extension-polkagate/src/util/types.ts (3)
APIs(810-810)DropdownOption(876-879)EndpointType(896-901)packages/extension-polkagate/src/util/misc.ts (1)
fastestConnection(167-193)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (4)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
8-9: Import alias mismatch flagged in previous review.Previous review identified that
endpointManager2.tsexportsEndpointManager2but you're importing it asEndpointManager. This remains unaddressed.
84-98: HardcodedisAuto: trueflagged in previous review.Previous review noted that line 89 hardcodes
isAuto: true, which is incorrect for manual endpoint selections. This issue remains unaddressed—the function signature should accept anisAutoparameter.
224-235: Storage write of default endpoint flagged in previous review.Line 234 writes the
AUTO_MODE_DEFAULT_ENDPOINTto storage when no endpoint exists. Previous review commented that this "can overwrite user settings during startup before storage load completes" and suggested treating the default as ephemeral. This was marked as "Addressed in commits a708d04 to adcf3d0" but the storage write is still present.
190-222: Handle unexpected endpoint formats.If
endpointValuedoesn't start with"wss"or"light"and isn't auto-mode (e.g.,"http://","ws://"), line 204 marks it as requested but no connection is initiated. This blocks future retries for that endpoint.Add a catch-all after line 221:
if (endpointValue.startsWith('light')) { connectToLightClient(genesisHash, endpointValue).catch(console.error); } + + // Unsupported endpoint format + if (!isAutoMode(endpointValue) && !endpointValue.startsWith('wss') && !endpointValue.startsWith('light')) { + console.warn('Unsupported endpoint format:', endpointValue); + // Clear request mark to allow retry + requestedQueue.current[genesisHash] = requestedQueue.current[genesisHash].filter((e) => e !== endpointValue); + } }, [connectToEndpoint, connectToLightClient, handleAutoMode]);
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (5)
packages/extension-polkagate/src/util/constants.ts (2)
AUTO_MODE(262-265)AUTO_MODE_DEFAULT_ENDPOINT(267-272)packages/extension-polkagate/src/class/endpointManager.ts (1)
EndpointManager(14-92)packages/extension-polkagate/src/util/types.ts (3)
APIs(810-810)DropdownOption(876-879)EndpointType(896-901)packages/extension-polkagate/src/util/misc.ts (1)
fastestConnection(167-193)packages/extension-polkagate/src/components/contexts.tsx (1)
APIContext(39-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: build
- GitHub Check: Analyze (javascript)
🔇 Additional comments (4)
packages/extension-ui/src/Popup/contexts/ApiProvider.tsx (4)
100-122: Well-implemented endpoint-specific resolution.The function now correctly resolves only the matching pending endpoint and clears the request flag, addressing previous feedback. The cleanup logic for empty genesisHash entries (lines 109-111) is also good practice.
165-176: Connection error handling is correct.The function properly resolves pending connections with the specific endpoint on both success and error paths, addressing previous review feedback.
178-188: Light client connection handling looks good.The implementation correctly mirrors
connectToEndpointwith appropriate error handling for the light client path.
254-271: Pending connection tracking correctly keyed by both genesisHash and endpoint.The implementation now properly keys pending connections by both genesisHash and endpoint (line 254, 268), addressing previous feedback about ensuring callers receive a promise for the correct endpoint.
Summary by CodeRabbit